/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Authors: Gou Pengfei
 *
 * Date: Aug. 2010
 *
 */

#ifndef __CPU_ATOMIC_EDGE_DYN_INST_HH__
#define __CPU_ATOMIC_EDGE_DYN_INST_HH__

#include "arch/isa_traits.hh"
#include "config/the_isa.hh"
#include "cpu/edge/atomic/base_atomic_dyn_inst.hh"
#include "cpu/inst_seq.hh"
#include "cpu/edge/static_inst.hh"

class Packet;

template <class Impl>
class AtomicEdgeDynInst : public BaseAtomicEdgeDynInst<Impl>
{
  public:
    /** Typedef for the CPU. */
    typedef typename Impl::CPU CPU;

    /** Binary machine instruction type. */
    typedef TheISA::MachInst MachInst;
    /** Extended machine instruction type. */
    typedef TheISA::ExtMachInst ExtMachInst;
    /** Logical register index type. */
    typedef TheISA::RegIndex RegIndex;
    /** Integer register index type. */
    typedef TheISA::IntReg   IntReg;
    typedef TheISA::FloatReg FloatReg;
    typedef TheISA::FloatRegBits FloatRegBits;
    /** Misc register index type. */
    typedef TheISA::MiscReg  MiscReg;

    typedef TheISA::ConsumerID ConsumerID;
    typedef TheISA::OpSize OpSize;
    typedef TheISA::BlockID BlockID;
    typedef TheISA::ChunkID ChunkID;
    typedef TheISA::InstID InstID;
    typedef TheISA::PredStatus PredStatus;
    typedef TheISA::DataflowTokenType DataflowTokenType;

    /// Operand of this instruction
    union Operand {
        // We only need one 64-bit buffer to store operand
        OpSize intValue;
        double doubleValue;
        float floatValue;
    };

    /// Instruction result
    union Result {
        // All of the result will be set into this 64-bit buffer
        // no matter it's an integer or float
        OpSize intResult;
        double doubleResult;
        float floatResult;
    };

    struct PredicationResult{
        PredStatus pred_status;
        bool valid;
    };

    struct Consumer{
        uint8_t type;
        uint8_t subtype;
        uint32_t id;
    };

    enum {
        MaxInstConsumers = TheISA::MaxInstConsumers,
        MaxInstOperands = TheISA::MaxInstOperands
    };

  public:
    /** BaseDynInst constructor given a binary instruction. */
    AtomicEdgeDynInst(EDGEStaticInstPtr staticInst, Addr PC, Addr NPC,
                 Addr Pred_PC, Addr Pred_NPC,
                 InstSeqNum seq_num, CPU *cpu, TheISA::BlockStatus blockstatus);

    /** BaseDynInst constructor given a binary instruction. */
    AtomicEdgeDynInst(ExtMachInst inst, Addr PC, Addr NPC,
                 Addr Pred_PC, Addr Pred_NPC,
                 InstSeqNum seq_num, CPU *cpu, TheISA::BlockStatus blockstatus);

    /** Executes the instruction.*/
    Fault execute();

    /** Initiates the access.  Only valid for memory operations. */
    Fault initiateAcc();

    /** Calculate the effective address for memory operations. */
    Fault eaComp();

    /** Completes the access.  Only valid for memory operations. */
    Fault completeAcc(PacketPtr pkt);

  private:
    /** Initializes variables. */
    void initVars();

  protected:

    /// Block ID this instruction belongs to
    BlockID _blockID;

    /// Chunk ID this instruction belongs to
    ChunkID _chunkID;

    /// Instruction ID inside a chunk
    InstID _instID;

    /// Data-flow token type indicates the output type of this inst
    DataflowTokenType  _dataflowTokenType;

    PredicationResult _predicationResult;

    Consumer _consumer[MaxInstConsumers];

    /// Operands of this instruction.
    Operand _operand[MaxInstOperands];
    bool operandValid[MaxInstOperands];
    
    Result _result;
    bool resultValid;

    /** Number of ready operands. */
    uint8_t readyOperands;

    /// A temp id for TEST insts to record the position in testInstPath.
    int8_t testInstPathID;

  public:

    int8_t getTestInstPathID() const {
        assert(this->staticInst->isTest());
        return testInstPathID;
    }

    void setTestInstPathID(int8_t val) {
        assert(this->staticInst->isTest());
        testInstPathID = val;
    }

    uint8_t getConsumerType( uint8_t idx) const {
        return _consumer[idx].type;
    }

    uint8_t getConsumerSubType ( uint8_t idx ) const {
        return _consumer[idx].subtype;
    }
    uint32_t getConsumerID ( uint8_t idx ) const {
        return _consumer[idx].id;
    }

    void setConsumerType ( uint8_t idx, uint8_t type ) {
        _consumer[idx].type = type;
    }

    void setConsumerSubType ( uint8_t idx, uint8_t sub_type ) {
        _consumer[idx].subtype = sub_type;
    }

    void setConsumerID ( uint8_t idx, uint32_t id ) {
        _consumer[idx].id = id;
    }

    OpSize getIntIQOperand(int i) const {
        assert(operandValid[i]);
        return _operand[i].intValue; 
    }

    double getFloatIQOperand(int i) {
        assert(operandValid[i]);
        TheISA::checkFloat64(_operand[i].intValue);
        return _operand[i].doubleValue;
    }

    float getFloat32IQOperand(int i) {
        assert(operandValid[i]);      
        TheISA::checkFloat32(_operand[i].intValue);    
        return _operand[i].floatValue;
    }

    ConsumerID getRawConsumerID(uint8_t idx) const 
    {
        return this->staticInst->getRawConsumerID(idx);
    }

    uint32_t getNumConsumers() const 
    {
        return this->staticInst->getNumConsumers(); 
    }

    uint32_t getNumOperands() const 
    {
        return this->staticInst->getNumOperands(); 
    }

    void setIntResult(OpSize  result) 
    {
        _result.intResult = result; 
        resultValid = true; 
    }

    void setFloatResult(double result) 
    {
        _result.doubleResult = result;
        resultValid = true;
    }

    void setFloat32Result(float result) {
        //Reinterpret the result to int
        _result.floatResult = result;
        resultValid = true;
    }

    bool setPredStatus(PredStatus status );

    void setIntOperand(int i, OpSize value) {
        _operand[i].intValue = value;
        operandValid[i] = true;
    }

    // Set float operand is the same as set Int operand
    // because we have the same buffer
    void setFloatOperand(int i, OpSize value) {
        _operand[i].intValue = value;
        operandValid[i] = true;
    }
    
    void markOperandReady();
    void markPredReady();

    int getReadyOperands() const { return readyOperands; }

    PredStatus getPredStatus() const {
        if (_predicationResult.valid ) return _predicationResult.pred_status;
        return TheISA::NoPred;
    }
    OpSize getIntResult() const{
        assert(resultValid);
        
        return _result.intResult;
    }
    OpSize getFloatResult() const{
        assert(resultValid);
        
        return _result.intResult;
    }

    /// Set EDGE Inst block related status
    void setInstID( InstID id) { _instID = id; }

    void setChunkID(ChunkID id) { _chunkID = id; }

    void setBlockID(BlockID id) { _blockID = id; }


    void setEDGEInstStatus(InstID inst_id, ChunkID chunk_id, BlockID block_id)
    {
        _instID = inst_id;
        _chunkID = chunk_id;
        _blockID = block_id;
    }
    InstID getInstID() const { return _instID; }

    ChunkID getChunkID() const { return _chunkID; }

    BlockID getBlockID() const { return _blockID; }

    void setDataflowTokenType( DataflowTokenType type )
    {
       _dataflowTokenType = type;
    }

    DataflowTokenType getDataflowTokenType() const
    { 
        return _dataflowTokenType; 
    }

  public:

    // The register accessor methods provide the index of the
    // instruction's operand (e.g., 0 or 1), not the architectural
    // register index, to simplify the implementation of register
    // renaming.  We find the architectural register index by indexing
    // into the instruction's own operand index table.  Note that a
    // raw pointer to the StaticInst is provided instead of a
    // ref-counted StaticInstPtr to redice overhead.  This is fine as
    // long as these methods don't copy the pointer into any long-term
    // storage (which is pretty hard to imagine they would have reason
    // to do).

    uint64_t readIntRegOperand(const EDGEStaticInst *si, int idx)
    {
        return this->thread->readIntReg(this->_srcRegIdx[idx]);
    }

    /** @todo: Make results into arrays so they can handle multiple dest
     *  registers.
     */
    void setIntRegOperand(const EDGEStaticInst *si, int idx, uint64_t val)
    {
        this->thread->setIntReg(this->_destRegIdx[idx], val);
    }
};

#endif // __CPU_ATOMIC_EDGE_DYN_INST_HH__

